bitkeeper revision 1.1159.1.164 (41503abcmVn6L0phzf-BOxlxa3zgxw)
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Tue, 21 Sep 2004 14:29:16 +0000 (14:29 +0000)
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Tue, 21 Sep 2004 14:29:16 +0000 (14:29 +0000)
Add support for reconfiguring an existing device,
including support for changing the bridge of a vif.
Treat bridge "null" as meaning no bridge.

tools/examples/network
tools/examples/vif-bridge
tools/python/xen/xend/XendClient.py
tools/python/xen/xend/XendDomain.py
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xend/server/SrvDomain.py
tools/python/xen/xend/server/controller.py
tools/python/xen/xend/server/netif.py
tools/python/xen/xend/sxp.py

index 6574399089f66fe36fe8f5f4e27484818ad881db..b679b93e43b0cf4c46d29a5c59ac5573a011b807 100755 (executable)
@@ -139,31 +139,44 @@ show_status () {
     echo '============================================================'
 }
 
+op_start () {
+    if [ "${bridge}" == "null" ] ; then
+        return
+    fi
+    # Create the bridge and give it the interface IP addresses.
+    # Move the interface routes onto the bridge.
+    create_bridge ${netdev} ${bridge}
+    transfer_addrs ${netdev} ${bridge}
+    transfer_routes ${netdev} ${bridge}
+    # Don't add $dev to $bridge if it's already on a bridge.
+    if ! brctl show | grep -q ${netdev} ; then
+        brctl addif ${bridge} ${netdev}
+    fi
+    
+    if [ ${antispoof} == 'yes' ] ; then
+        antispoofing ${netdev} ${bridge}
+    fi
+}
+
+op_stop () {
+    if [ "${bridge}" == "null" ] ; then
+        return
+    fi
+    # Remove the interface from the bridge.
+    # Move the routes back to the interface.
+    brctl delif ${bridge} ${netdev}
+    transfer_routes ${bridge} ${netdev}
+
+    # It's not our place to be enabling forwarding...
+}
+
 case ${OP} in
     start)
-        # Create the bridge and give it the interface IP addresses.
-        # Move the interface routes onto the bridge.
-        create_bridge ${netdev} ${bridge}
-        transfer_addrs ${netdev} ${bridge}
-        transfer_routes ${netdev} ${bridge}
-       # Don't add $dev to $bridge if it's already on a bridge.
-       if ! brctl show | grep -q ${netdev} ; then
-           brctl addif ${bridge} ${netdev}
-       fi
-        
-        if [ ${antispoof} == 'yes' ] ; then
-            antispoofing ${netdev} ${bridge}
-        fi
-        
+        op_start
         ;;
     
     stop)
-        # Remove the interface from the bridge.
-        # Move the routes back to the interface.
-        brctl delif ${bridge} ${netdev}
-        transfer_routes ${bridge} ${netdev}
-
-        # It's not our place to be enabling forwarding...
+        op_stop
         ;;
 
     status)
index 2138aa7b9403eeb739f84289761c13a2e4661f4f..505ac1ec51c332aec55da8bd6ec90492d992e86a 100755 (executable)
@@ -69,6 +69,11 @@ case $OP in
         ;;
 esac
 
+# Don't do anything if the bridge is "null".
+if [ "${bridge}" == "null" ] ; then
+    exit
+fi
+
 # Add/remove vif to/from bridge.
 brctl ${brcmd} ${bridge} ${vif}
 
index 01d6d3df3cff7e1fde0b2bd0b55f9330f27bb9bb..958742d168e6ebec8c4581663964a5f88787c27f 100644 (file)
@@ -550,6 +550,12 @@ class Xend:
                               'type'    : type,
                               'idx'     : idx })
 
+    def xend_domain_device_configure(self, id, config, idx):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'device_configure',
+                              'idx'     : idx,
+                              'config'  : fileof(config) })
+
     def xend_consoles(self):
         return self.xendGet(self.consoleurl())
 
index afe2a4601bfb23ae96314f7d63be54def56c3dd8..8aa33484f1f22003eafba989959e8e1889e6969f 100644 (file)
@@ -684,12 +684,27 @@ class XendDomain:
         self.update_domain(dominfo.id)
         return val
 
+    def domain_device_configure(self, id, devconfig, idx):
+        """Configure an existing device for a domain.
+
+        @param id:   domain id
+        @param devconfig: device configuration
+        @param idx:  device index
+        @return: updated device configuration
+        """
+        dominfo = self.domain_lookup(id)
+        self.refresh_schedule()
+        val = dominfo.device_configure(devconfig, idx)
+        self.update_domain(dominfo.id)
+        return val
+    
+
     def domain_device_destroy(self, id, type, idx):
         """Destroy a device.
 
         @param id:  domain id
-        @param type: device type
         @param idx:  device index
+        @param type: device type
         """
         dominfo = self.domain_lookup(id)
         self.refresh_schedule()
@@ -706,7 +721,6 @@ class XendDomain:
         """
         dominfo = self.domain_lookup(id)
         devs = dominfo.get_devices(type)
-        #return range(0, len(devs))
         return devs
 
     def domain_devtype_get(self, id, type, idx):
index f582a511455ff3bd6d83719e3f357a26400f54f6..17a002c6653c8da0413f40e4abe50d7a8244c963 100644 (file)
@@ -825,6 +825,30 @@ class XendDomainInfo:
         d = dev_handler(self, dev_config, dev_index, change=1)
         return d
 
+    def device_configure(self, dev_config, idx):
+        """Configure an existing device.
+
+        @param dev_config: device configuration
+        @param idx:  device index
+        """
+        type = sxp.name(dev_config)
+        dev = self.get_device_by_index(type, idx)
+        if not dev:
+            raise VmError('invalid device: %s %s' % (type, idx))
+        new_config = dev.configure(dev_config, change=1)
+        devs = self.devices.get(type)
+        index = devs.index(dev)
+        # Patch new config into device configs.
+        dev_configs = self.config_devices(type)
+        old_config = dev_configs[index]
+        dev_configs[index] = new_config
+        # Patch new config into vm config.
+        new_full_config = ['device', new_config]
+        old_full_config = ['device', old_config]
+        old_index = self.config.index(old_full_config)
+        self.config[old_index] = new_full_config
+        return new_config
+        
     def device_destroy(self, type, idx):
         """Destroy a device.
 
index bdd57524cea03331c062d6f0d4771f82f18e88ce..a0ec17526f346e3390508cb96a8833c31fbb6df5 100644 (file)
@@ -164,6 +164,14 @@ class SrvDomain(SrvDir):
         val = fn(req.args, {'dom': self.dom.id})
         return val
                 
+    def op_device_configure(self, op, req):
+        fn = FormFn(self.xd.domain_device_configure,
+                    [['dom', 'str'],
+                     ['config', 'sxpr'],
+                     ['idx', 'str']])
+        d = fn(req.args, {'dom': self.dom.id})
+        return d
+
     def op_vifs(self, op, req):
         devs = self.xd.domain_vif_ls(self.dom.id)
         return [ dev.sxpr() for dev in devs ]
index d77f51c10ced70ab3ab32dbef9deba8f00bbfc3e..71855d87555ba0b050af9857bafff8b507980ad3 100755 (executable)
@@ -617,6 +617,9 @@ class Dev:
         """
         raise NotImplementedError()
 
+    def configure(self, config, change=0):
+        raise NotImplementedError()
+
 class SplitDev(Dev):
 
     def __init__(self, idx, controller):
index 3c07333a8496da0ac8d56dc62d2b99167c36240e..84f569d830467eb766182bb9a96bddf9e05b57aa 100755 (executable)
@@ -104,31 +104,82 @@ class NetDev(controller.SplitDev):
         self.evtchn = None
         self.configure(config)
 
-    def configure(self, config):
+    def _get_config_mac(self, config):
+        vmac = sxp.child_value(config, 'mac')
+        if not vmac: return None
+        mac = [ int(x, 16) for x in vmac.split(':') ]
+        if len(mac) != 6: raise XendError("invalid mac")
+        return mac
+
+    def _get_config_ipaddr(self, config):
+        ips = sxp.children(config, elt='ip')
+        if ips:
+            val = []
+            for ipaddr in ips:
+                val.append(sxp.child0(ipaddr))
+        else:
+            val = None
+        return val
+
+    def configure(self, config, change=0):
+        if change:
+            return self.reconfigure(config)
         self.config = config
         self.mac = None
         self.bridge = None
         self.script = None
         self.ipaddr = []
-        
-        vmac = sxp.child_value(config, 'mac')
-        if not vmac: raise XendError("invalid mac")
-        mac = [ int(x, 16) for x in vmac.split(':') ]
-        if len(mac) != 6: raise XendError("invalid mac")
-        self.mac = mac
 
+        mac = self._get_config_mac(config)
+        if mac is None:
+            raise XendError("invalid mac")
+        self.mac = mac
         self.bridge = sxp.child_value(config, 'bridge')
         self.script = sxp.child_value(config, 'script')
-
-        ipaddrs = sxp.children(config, elt='ip')
-        for ipaddr in ipaddrs:
-            self.ipaddr.append(sxp.child0(ipaddr))
+        self.ipaddr = self._get_config_ipaddr(config) or []
         
         try:
             self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
         except:
             raise XendError('invalid backend domain')
 
+    def reconfigure(self, config):
+        """Reconfigure the interface with new values.
+        Not all configuration parameters can be changed:
+        bridge, script and ip addresses can,
+        backend and mac cannot.
+
+        To leave a parameter unchanged, omit it from the changes.
+
+        @param config configuration changes
+        @return updated interface configuration
+        @raise XendError on errors
+        """
+        changes = {}
+        mac = self._get_config_mac(config)
+        bridge = sxp.child_value(config, 'bridge')
+        script = sxp.child_value(config, 'script')
+        ipaddr = self._get_config_ipaddr(config)
+        backendDomain = sxp.child_value(config, 'backend', '0')
+        if (mac is not None) and (mac != self.mac):
+            raise XendError("cannot change mac")
+        if (backendDomain is not None) and (backendDomain != str(self.backendDomain)):
+            raise XendError("cannot change backend")
+        if (bridge is not None) and (bridge != self.bridge):
+            changes['bridge'] = bridge
+        if (script is not None) and (script != self.script):
+            changes['script'] = script
+        if (ipaddr is not None) and (ipaddr != self.ipaddr):
+            changes['ipaddr'] = ipaddr
+
+        if changes:
+            self.vifctl("down")
+            for (k, v) in changes.items():
+                setattr(self, k, v)
+            self.config = sxp.merge(config, self.config)
+            self.vifctl("up")
+        return self.config
+
     def sxpr(self):
         vif = str(self.vif)
         mac = self.get_mac()
index 4fd9e9a92beae4f9d2913517e0635ea55d972814..98cc30341656f33fc85fc5d1cb5758d21f52e9df 100644 (file)
@@ -41,7 +41,8 @@ __all__ = [
     "has_id", 
     "with_id", 
     "child_with_id", 
-    "elements", 
+    "elements",
+    "merge",
     "to_string",
     "from_string",
     "all_from_string",
@@ -595,6 +596,77 @@ def elements(sxpr, ctxt=None):
                 yield v
         i += 1
 
+def merge(s1, s2):
+    """Merge sxprs s1 and s2.
+    Returns an sxpr containing all the fields from s1 and s2, with
+    entries in s1 overriding s2. Recursively merges fields.
+
+    @param s1 sxpr
+    @param s2 sxpr
+    @return merged sxpr
+    """
+    if s1 is None:
+        val = s2
+    elif s2 is None:
+        val = s1
+    elif elementp(s1):
+        name1 = name(s1)
+        (m1, v1) = child_map(s1)
+        (m2, v2) = child_map(s2)
+        val = [name1]
+        for (k1, f1) in m1.items():
+            merge_list(val, f1, m2.get(k1, []))
+        for (k2, f2) in m2.items():
+            if k2 in m1: continue
+            val.extend(f2)
+        val.extend(v1)
+    else:
+        val = s1
+    return val
+
+def merge_list(sxpr, l1, l2):
+    """Merge element lists l1 and l2 into sxpr.
+    The lists l1 and l2 are all element with the same name.
+    Values from l1 are merged with values in l2 and stored in sxpr.
+    If one list is longer than the other the excess values are used
+    as they are.
+
+    @param sxpr to merge into
+    @param l1 sxpr list
+    @param l2 sxpr list
+    @return modified sxpr
+    """
+    n1 = len(l1)
+    n2 = len(l2)
+    nmin = min(n1, n2)
+    for i in range(0, nmin):
+        sxpr.append(merge(l1[i], l2[i]))
+    for i in range(nmin, n1):
+        sxpr.append(l1[i])
+    for i in range(nmin, n2):
+        sxpr.append(l2[i])
+    return sxpr
+
+def child_map(sxpr):
+    """Get a dict of the elements in sxpr and a list of its values.
+    The dict maps element name to the list of elements with that name,
+    and the list is the non-element children.
+
+    @param sxpr
+    @return (dict, list)
+    """
+    m = {}
+    v = []
+    for x in children(sxpr):
+        if elementp(x):
+            n = name(x)
+            l = m.get(n, [])
+            l.append(x)
+            m[n] = l
+        else:
+            v.append(x)
+    return (m, v)
+
 def to_string(sxpr):
     """Convert an sxpr to a string.